/***************************************************************************
 *   source.c                                                              *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/

#include "header.h"

float4 vect = MGPI_DEFAULT_VECTOR;
float scattering = 0.45f;
float heightScale = 0.045f;

// NOTE: On Linux replace ALL 'ct.component.' with 'MGPI_GetRootComponent()->'

/**
 * Skybox transformation procedure.
 */
static void skybox_trans(MGPI_RendNode *node) {
	int hcamera = ct.component.flags[0];
	//int hcamera = MGPI_GetRootComponent()->flags[0];
    float4 eye = MGPI_DEFAULT_VECTOR;
    g3dGetCamVect(&vect, hcamera, MGPI_CAMERA_POSITION);

    glTranslatef(eye[X], eye[Y], eye[Z]);
    glScalef(1000.0f, 1000.0f, 1000.0f);
    return;
}

/**
 * User-defined rendering object transformation procedure.
 */
static void user_defined_trans(MGPI_RendNode *node) {
	glTranslatef(0, -1.5f, -10);
	glScalef(3, 3, 3);
	g3dDrawCoordinates();

	return;
}

static void obj_trans(MGPI_RendNode *node) {
	glTranslatef(0, -1.5f, -5);
	//glScalef(1.0f, 50.0f, 1.0f);
	glScalef(0.05f, 0.05f, 0.05f);
	g3dDrawCoordinates();

	return;
}

/**
 * User-defined rendering procedure.
 */
static int user_defined(MGPI_RendNode *node) {
	Roeskva_t *ctx = (Roeskva_t *)ct.component.flags[1];
	//Roeskva_t *ctx = (Roeskva_t *)MGPI_GetRootComponent()->flags[1];

	float4 colord = { 0.1, 0.1, 0.1, 1 };
	float4 colors = { 0.1, 0.1, 0.1, 1 };
	int index = 0;

	glEnable(GL_LIGHTING);
	glEnable(GL_LIGHT0);
	glLightfv(GL_LIGHT0, GL_DIFFUSE, colord);
	glLightfv(GL_LIGHT0, GL_SPECULAR, colors);
	glLightf(GL_LIGHT0, GL_CONSTANT_ATTENUATION, 0.0f);
	glLightf(GL_LIGHT0, GL_LINEAR_ATTENUATION, 0.0f);
	glLightf(GL_LIGHT0, GL_QUADRATIC_ATTENUATION, 0.005f);
	glMaterialf(GL_FRONT, GL_SHININESS, 600.0f);
	//{
	//	glActiveTexture(GL_TEXTURE0);
	//	glMatrixMode(GL_TEXTURE);
	//	glPushMatrix();
	//	{
	//		glLoadIdentity();
	//		glScalef(3, 3, 1);

	//		glMatrixMode(GL_MODELVIEW);

	//		g3dDrawTeapot(1, -1, node->shaders.program_id);
	//		
	//		glMatrixMode(GL_TEXTURE);
	//	}
	//	glPopMatrix();
	//	glMatrixMode(GL_MODELVIEW);
	//}
	glDisable(GL_LIGHT0);
	glDisable(GL_LIGHTING);
	return 0;
}


/**
 * Function called at the engine stop, just before shot down. In here one
 * can put the finalization code e.g. releasing resources.
 */
void ENGINE_Exit(void) {

	// And when the time comes all shall fade away...
    MGPI_ENGINE_STOP
    MGPI_Exit();

	// NOTE: no access to engine exec. context!

	getchar();
    return;
}

/**
 * A initialization function, called at the begining of the application. In here
 * one can put any code concerning loading objects, preparing the scene etc.
 */
void ENGINE_Init(Roeskva_t *ctx) {
	SDL_Surface *imgs[6];
	GLubyte *pixels[6];
	char *code[8];
	GLuint ids[3], targs[3] = { GL_TEXTURE_2D, GL_TEXTURE_2D, GL_TEXTURE_2D };
	char *names[3];
	int progs[8];
    {
		// Basic definition before we start
        MGPI_ENGINE_CREATE_CAMERA(MGPI_PERSPECTIVE);
		ctx->setProperty(MGPI_CAMERA_VIEWPORT, MGPI_ENGINE_CAMERA, SCREEN_WIDTH, SCREEN_HEIGHT);
		ctx->setup(MGPI_SETUP_BUFFERS);

	} MGPI_ENGINE_START {
		// Comment if building on Linux
		ct.component.onEvent[MGPI_KEY_DOWN] = keyProc;
		ct.component.onEvent[MGPI_MOUSE_MOTION] = mouseMotionProc;
		ct.component.onEvent[MGPI_MOUSE_BUTTON_DOWN] = mouseButtonProc;

		// Uncomment if building on Linux
		//MGPI_InitContainer(NULL);
		//MGPI_GetRootComponent()->onEvent[MGPI_KEY_DOWN] = keyProc;
		//MGPI_GetRootComponent()->onEvent[MGPI_MOUSE_MOTION] = mouseMotionProc;
		//MGPI_GetRootComponent()->onEvent[MGPI_MOUSE_BUTTON_DOWN] = mouseButtonProc;
        //MGPI_InitEvents(MGPI_THREAD_EVENTS, NULL);

		// Pass essential info to the game container
		ct.component.flags[0] = MGPI_ENGINE_CAMERA;
		ct.component.flags[1] = (int)ctx;
		//MGPI_GetRootComponent()->flags[0] = MGPI_ENGINE_CAMERA;
		//MGPI_GetRootComponent()->flags[1] = (int)ctx;

		// Don't show the cursor
		SDL_ShowCursor(SDL_DISABLE);

		// Skybox configuration
		{
			// Load skybox
			MGPI_ENGINE_HANDLE8 = ctx->loadObject(NULL, MGPI_SKYBOX_CUBEMAP);

			// Load textures for the skybox
			imgs[0] = g2dLoadImage("data/pos_x.png");
			pixels[0] = imgs[0]->pixels;

			imgs[1] = g2dLoadImage("data/neg_x.png");
			pixels[1] = imgs[1]->pixels;

			imgs[2] = g2dLoadImage("data/pos_y.png");
			pixels[2] = imgs[2]->pixels;

			imgs[3] = g2dLoadImage("data/neg_y.png");
			pixels[3] = imgs[3]->pixels;

			imgs[4] = g2dLoadImage("data/pos_z.png");
			pixels[4] = imgs[4]->pixels;

			imgs[5] = g2dLoadImage("data/neg_z.png");
			pixels[5] = imgs[5]->pixels;

			// Generate a single cube map texture
			MGPI_ENGINE_HANDLE0 = g3dGenTextureCubeRGB(pixels, imgs[0]->w, imgs[0]->h);

			// Setup the skybox rendering object
			ctx->selectObject(MGPI_ENGINE_HANDLE8);
			ctx->setProperty(MGPI_TRANSFORMATIONS, skybox_trans);
			ctx->setProperty(MGPI_TEXTURE, MGPI_ENGINE_HANDLE0, GL_TEXTURE_CUBE_MAP);
			
			// Free the images of the skaybox
			SDL_FreeSurface(imgs[0]);
			SDL_FreeSurface(imgs[1]);
			SDL_FreeSurface(imgs[2]);
			SDL_FreeSurface(imgs[3]);
			SDL_FreeSurface(imgs[4]);
			SDL_FreeSurface(imgs[5]);
		}

		// Engine shaders configuration & loading of OBJs
		{
			// Start shaders engine
			ctx->setProperty(MGPI_SHADERS);

			// Load shaders code
			code[0] = (char *)fload("data/BumpMapping.frag");
			code[1] = (char *)fload("data/PhongBlinn.frag");
			code[2] = (char *)fload("data/mainFrag.frag");
			code[3] = (char *)fload("data/Tangent.vert");

			// Create and compile fragment and vertex shaders
			MGPI_ENGINE_CREATE_SHADER(MGPI_ENGINE_HANDLE0, MGPI_FRAGMENT_SHADER, 3, code, NULL);
			MGPI_ENGINE_CREATE_SHADER(MGPI_ENGINE_HANDLE1, MGPI_VERTEX_SHADER, 1, code + 3, NULL);

			// Define and select a user-defined rendering object
			MGPI_ENGINE_SELECT_NEW_OBJECT(MGPI_ENGINE_HANDLE9, MGPI_USER_DEFINED, NULL);
			ctx->setProperty(MGPI_TRANSFORMATIONS, user_defined_trans);
			ctx->setProperty(MGPI_USER_DEFINED, user_defined);

			// Configure a shader program
			ctx->selectProgram(MGPI_DEFAULT);
			MGPI_ENGINE_HANDLE2 = ctx->setupProgram(MGPI_ENGINE_HANDLES, 2);

			// Pass the program ID and effects commands to the selected rendering object
			ctx->setProperty(MGPI_SHADER_PROGRAM, MGPI_ENGINE_HANDLE2);
			ctx->setProperty(MGPI_GL_COMMANDS, MGPI_GL_CMD_COMBINED, 2, MGPI_GL_CMD_SHADERS, MGPI_GL_CMD_MULTITEXTURE);

			// Setup uniform variables loacation and value
			ctx->getProperty(MGPI_UNIFORM_LOCATION, "fvEyePosition", &MGPI_ENGINE_HANDLE3);
			ctx->setProperty(MGPI_UNIFORM_VALUE, MGPI_ENGINE_HANDLE3, MGPI_GLSL_FLOATnv, vect, 3);

			ctx->getProperty(MGPI_UNIFORM_LOCATION, "fScattering", &MGPI_ENGINE_HANDLE4);
			ctx->setProperty(MGPI_UNIFORM_VALUE, MGPI_ENGINE_HANDLE4, MGPI_GLSL_FLOAT1, &scattering, 1);

			ctx->getProperty(MGPI_UNIFORM_LOCATION, "fHeightScale", &MGPI_ENGINE_HANDLE5);
			ctx->setProperty(MGPI_UNIFORM_VALUE, MGPI_ENGINE_HANDLE5, MGPI_GLSL_FLOAT1, &heightScale, 1);

			// Load textures for multitexturing
			names[0] = "bumpMap";
			ids[0] = g3dTexture2DType("data/mur_NormalMap.png", GL_RGBA);//"rocks_NM_height_2.png", GL_RGBA);//"upnm.png", GL_RGB);//

			names[1] = "baseMap";
			ids[1] = g3dTexture2DType("data/blank.jpg", GL_RGB);//"rockbump.jpg", GL_RGB);//"mur_Ambiant.png", GL_RGB);//"ninja_head_AO.jpg", GL_RGB);//"rustbin.jpg", GL_RGB);//

			names[2] = "specularMap";
			ids[2] = g3dTexture2DType("data/mur_Hauteur.png", GL_RGB);//"rocks_NM_height2.jpg", GL_RGB);//"ninja_head_DM.jpg", GL_RGB);//"blank.jpg", GL_RGB);//

			// Pass the taxtures to the selected rendering object
			ctx->setProperty(MGPI_MULTITEXTURE, 3, names, targs, ids);

			// Load OBJ model
			MGPI_ENGINE_SELECT_NEW_OBJECT(MGPI_ENGINE_HANDLE10, MGPI_MODEL_OBJ, "data/models/ninjaHead.obj");//"models/cube.obj");//"models/sphere.obj");//"models/teapot.obj");//"models/trashbin.obj");//"models/TesselatedPlane.obj");//"models/Torus2.obj");//

			ctx->setProperty(MGPI_TRANSFORMATIONS, obj_trans);
			ctx->setProperty(MGPI_SHADER_PROGRAM, MGPI_ENGINE_HANDLE2);
			ctx->setProperty(MGPI_GL_COMMANDS, MGPI_GL_CMD_COMBINED, 2, MGPI_GL_CMD_SHADERS, MGPI_GL_CMD_MULTITEXTURE);
			//ctx->setProperty(MGPI_UNIFORM_VALUE, MGPI_ENGINE_HANDLE3, MGPI_GLSL_FLOATnv, vect, 3);
			ctx->setProperty(MGPI_MULTITEXTURE, 3, names, targs, ids);
		}

		// Framebuffer configuration
		{
			// Setting up depth and color internal formats
			ctx->setProperty(MGPI_DEPTH, GL_DEPTH_COMPONENT32);
			ctx->setProperty(MGPI_COLOR, GL_RGBA);//32F_ARB);

			// Setting up numer of samples in multisampling antialiasing
			ctx->setProperty(MGPI_MULTISAMPLE, 4);

			// Setting up buffer for multisample antialiasing
			ctx->setProperty(MGPI_FRAMEBUFFER_MULTISAMPLE, SCREEN_WIDTH, SCREEN_HEIGHT);

			// Frist color texture
			MGPI_ENGINE_HANDLE11 = g3dGenEmptyTexture2D(SCREEN_WIDTH, 
														SCREEN_HEIGHT, 
														GL_FLOAT, 
														GL_RGBA,//32F_ARB,
														GL_RGBA, 
														GL_TEXTURE_2D);

			ctx->setProperty(MGPI_FRAMEBUFFER, 
							 GL_TEXTURE_2D, 
							 GL_COLOR_ATTACHMENT0_EXT, 
							 MGPI_DEFAULT_FRAMEBUFFER, 
							 MGPI_ENGINE_HANDLE11,
							 SCREEN_WIDTH,
							 SCREEN_HEIGHT);

			// Second color texture
			MGPI_ENGINE_HANDLE12 = g3dGenEmptyTexture2D(SCREEN_WIDTH, 
														SCREEN_HEIGHT, 
														GL_FLOAT, 
														GL_RGBA,//32F_ARB,
														GL_RGBA, 
														GL_TEXTURE_2D);

			ctx->setProperty(MGPI_FRAMEBUFFER, 
							 GL_TEXTURE_2D, 
							 GL_COLOR_ATTACHMENT0_EXT,
							 MGPI_DEFAULT_FRAMEBUFFER+1, 
							 MGPI_ENGINE_HANDLE12,
							 SCREEN_WIDTH,
							 SCREEN_HEIGHT);

			// Depth texture
			MGPI_ENGINE_HANDLE13 = g3dGenEmptyTexture2D(SCREEN_WIDTH, 
														SCREEN_HEIGHT, 
														GL_UNSIGNED_INT, 
														GL_DEPTH_COMPONENT32,
														GL_DEPTH_COMPONENT, 
														GL_TEXTURE_2D);

			ctx->setProperty(MGPI_FRAMEBUFFER, 
							 GL_TEXTURE_2D, 
							 GL_DEPTH_ATTACHMENT_EXT, 
							 MGPI_DEFAULT_FRAMEBUFFER, 
							 MGPI_ENGINE_HANDLE13,
							 SCREEN_WIDTH,
							 SCREEN_HEIGHT);	

			// Setup screen-aligned quad for the framebuffer texture rendering
			MGPI_ENGINE_SELECT_NEW_OBJECT(MGPI_ENGINE_HANDLE14, MGPI_SCREEN_QUAD, NULL);
			{
				code[4] = (char *)fload("data/BrightPass.frag");
				code[5] = (char *)fload("data/ScreenAlignedQuad.vert");

				MGPI_ENGINE_CREATE_SHADER(MGPI_ENGINE_HANDLE0, MGPI_FRAGMENT_SHADER, 1, code + 4, NULL);
				MGPI_ENGINE_CREATE_SHADER(MGPI_ENGINE_HANDLE1, MGPI_VERTEX_SHADER, 1, code + 5, NULL);

				ctx->selectProgram(MGPI_OBJECT);
				progs[0] = ctx->setupProgram(MGPI_ENGINE_HANDLES, 2);
				ctx->setProperty(MGPI_SHADER_PROGRAM, progs[0]);
				ctx->setProperty(MGPI_GL_COMMANDS, MGPI_GL_CMD_COMBINED, 2, MGPI_GL_CMD_SHADERS, MGPI_GL_CMD_TEXTURE);
				ctx->setProperty(MGPI_TEXTURE, MGPI_ENGINE_HANDLE11, GL_TEXTURE_2D);
			}

			// Setup another screen-aligned quad for the framebuffer texture rendering (second pass)
			MGPI_ENGINE_SELECT_NEW_OBJECT(MGPI_ENGINE_HANDLE15, MGPI_SCREEN_QUAD, NULL);
			{
				code[4] = (char *)fload("data/PostGlow.frag");
				//code[4] = (char *)fload("data/DirectionalBlur.frag");
				//code[4] = (char *)fload("data/SmartBlur.frag");

				names[0] = "frame";
				names[1] = "bright";
				//names[0] = "frame";
				//names[1] = "pass";

				ids[0] = MGPI_ENGINE_HANDLE11;
				ids[1] = MGPI_ENGINE_HANDLE12;

				MGPI_ENGINE_CREATE_SHADER(MGPI_ENGINE_HANDLE0, MGPI_FRAGMENT_SHADER, 1, code + 4, NULL);

				ctx->selectProgram(MGPI_OBJECT);
				progs[1] = ctx->setupProgram(MGPI_ENGINE_HANDLES, 2);
				ctx->setProperty(MGPI_SHADER_PROGRAM, progs[1]);
				ctx->setProperty(MGPI_GL_COMMANDS, MGPI_GL_CMD_COMBINED, 2, MGPI_GL_CMD_SHADERS, MGPI_GL_CMD_MULTITEXTURE);
				ctx->setProperty(MGPI_MULTITEXTURE, 2, names, targs, ids);
			}
		}
	}
    return;
}

/**
 * The engine main rendering loop function. Place here any code refering
 * to rendering process. Note that calling here any logic processing functions
 * is not advisable.
 */
void ENGINE_Render(Roeskva_t *ctx) {
	float4 pos = MGPI_DEFAULT_VECTOR;
	pos[X] = posX * 10;
	pos[Y] = posY * 10;
	pos[Z] = posZ * 10;

	glEnable(GL_LIGHTING);
	glEnable(GL_LIGHT0);
	glLightfv(GL_LIGHT0, GL_POSITION, pos);
	glDisable(GL_LIGHT0);
	glDisable(GL_LIGHTING);
	
	g3dGetCamVect(&vect, ctx->hcamera, MGPI_CAMERA_POSITION);
    ctx->updateCamera(MGPI_ENGINE_CAMERA);
    {
		ctx->setProperty(MGPI_FRAMEBUFFER, MGPI_DEFAULT_FRAMEBUFFER);
		ctx->setProperty(MGPI_RENDER_TARGET, MGPI_TEXTURE, MGPI_ENABLE);
		{
			glPushMatrix();
			glTranslatef(pos[X], pos[Y], pos[Z]);
			g3dDrawCoordinates();
			glPopMatrix();

			// Render skybox
			ctx->renderObject(MGPI_ENGINE_HANDLE8);

			// Render object with bumpmapping
			ctx->renderObject(MGPI_ENGINE_HANDLE9);
			ctx->renderObject(MGPI_ENGINE_HANDLE10);

			// Render global triad and white grid
			g3dDrawCoordinates();
			g3dDrawGrid(50.0f, 1.0f, -2.0f, 0, 1, 1);
		}
		ctx->setProperty(MGPI_RENDER_TARGET, MGPI_TEXTURE, MGPI_DISABLE);

		ctx->setProperty(MGPI_FRAMEBUFFER, MGPI_DEFAULT_FRAMEBUFFER+1);
		ctx->setProperty(MGPI_RENDER_TARGET, MGPI_TEXTURE, MGPI_ENABLE);
		{
			ctx->renderObject(MGPI_ENGINE_HANDLE14);	// bright-pass
		}
		ctx->setProperty(MGPI_RENDER_TARGET, MGPI_TEXTURE, MGPI_DISABLE);

		ctx->renderObject(MGPI_ENGINE_HANDLE15);		// post-glow

		// Render diagnostic informations
		ctx->renderDiagnostic();
    }
    ctx->commitChanges();
		
    return;
}
